package es.udc.cameraar2;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicYuvToRGB;
import android.renderscript.Type;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.HybridBinarizer;

/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
		SurfaceHolder.Callback {
	private static final String TAG = "CameraPreview";
	private SurfaceHolder mHolder;
	private Camera mCamera;
	private Panel panel;
	private Canvas c;
	private Context mContext;
	private byte[] mBuffer;
	private static int width, height;
	private static int framewidth, frameheight;
	private float distance = 1;
	private float minx = 0, miny = 0, maxx = 0, maxy = 0;
	private int count = 0;
	private int refreshRate = 10;
		
	private int pointsCounter = 0;
	
	private ThreadGetData thread = null;

	private ResultPoint[] previousResultPoints;
	protected long timestamp;
	private int timesPerSec;
	protected long timestampCanvas = 0;
	protected long resultTimeStamp = 0;
		
	public CameraPreview(Context context, Camera camera, Panel p) {
		super(context);
		mContext = context;
		mCamera = camera;
		panel = p;

		// Install a SurfaceHolder.Callback so we get notified when the
		// underlying surface is created and destroyed.
		mHolder = getHolder();
		mHolder.addCallback(this);
		// deprecated setting, but required on Android versions prior to 3.0
		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		
		timesPerSec = 100/refreshRate;
					
	}
	
	
	public void surfaceCreated(SurfaceHolder holder) {
		// The Surface has been created, now tell the camera where to draw the
		// preview.
		panel.setPreview(this);
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		mCamera.stopPreview();
		mCamera = null;
	}

	
	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		// If your preview can change or rotate, take care of those events here.
		// Make sure to stop the preview before resizing or reformatting it.

		width = w;
		height = h;

		if (mHolder.getSurface() == null) {
			// preview surface does not exist
			return;
		}

		// stop preview before making changes
		try {
			mCamera.stopPreview();
		} catch (Exception e) {
			// ignore: tried to stop a non-existent preview
		}

		// set preview size and make any resize, rotate or
		// reformatting changes here

		// start preview with new settings
		try {
			Camera.Parameters parameters = mCamera.getParameters();
			parameters.setPreviewSize(w, h);
			mCamera.setParameters(parameters);
			mBuffer = new byte[mCamera.getParameters().getPictureSize().height
					* mCamera.getParameters().getPictureSize().width];
			// mCamera.addCallbackBuffer(mBuffer);
			mCamera.setPreviewDisplay(mHolder);
			mCamera.startPreview();

			mCamera.setPreviewCallback(new PreviewCallback() {
				
				@SuppressLint("WrongCall")
				public void onPreviewFrame(byte[] data, Camera arg1) {

					
					//Si ha pasado el tiempo de refresco definido calculamos la posicion del QR
					if ((System.currentTimeMillis()-timestamp) > timesPerSec) {
						
						timestamp=System.currentTimeMillis();
						Result result = null;

						byte[] dataModified = data;

						if (pointsCounter == 0) {
							previousResultPoints = new ResultPoint[] { new ResultPoint(0, 0) };
							framewidth = width;
							frameheight = height;
						} else {
							dataModified = calculateSmallFrame(data,previousResultPoints);
						}

						//BinaryBitmap bitmap = createNewBinaryBitmap(data);
												

						/*
						 * Comprobar si va mas rapido con QRCodeReader que con
						 * el Multiformat
						 */
						 //Reader reader = new MultiFormatReader();
						//Reader reader = new QRCodeReader();

						try {

							//result = reader.decode(bitmap);
							// String contents = result.getText();
							// byte[] rawBytes = result.getRawBytes();
							// BarcodeFormat format = result.getBarcodeFormat();
							ResultPoint[] points = result.getResultPoints();
							if (result != null) {
								previousResultPoints = points;
								pointsCounter = 3;
								if (thread==null){
									thread = new ThreadGetData(result.getText());
								}
							}

							// Log.d("CAMERAPREVIEW","#############Valores result    :"+
							// result.getText()+ "     ###########");
							panel.setText(result.getText() + "  -Pcounter: "
									+ pointsCounter);

							/*
							 * Context context = getContext(); int duration =
							 * 50; Toast toast;^
							 */

							// toast = Toast.makeText(context,result.getText(),
							// duration);
							// toast.show();

						} catch (Exception e) {
							Log.d("CAMERAPREVIEW", "Error   :" + e.toString());
							pointsCounter--;
						}
						// }

						Canvas canvas = null;
						try {
							canvas = panel.getHolder().lockCanvas(null);
							canvas.drawColor(0, PorterDuff.Mode.CLEAR);
							synchronized (panel.getHolder()) {
								// Log.d("THREADCANVAS","Llamo a OnDRAW");
								if (canvas != null) {
									timestampCanvas = System.currentTimeMillis();
									panel.onDraw(canvas, result);
									resultTimeStamp = System.currentTimeMillis()-timestamp;
								}
							}
						} finally {
							if (canvas != null)
								panel.getHolder().unlockCanvasAndPost(canvas);
						}
					}
					else count = 2;
				}

				private BinaryBitmap createNewBinaryBitmap(byte[] dataModified) {
					
					RenderScript rs; 
					ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
					byte[] yuvByteArray = new byte[291600];
			        byte[] outBytes = new byte[width * height * 4];

			        
					rs = RenderScript.create(mContext);
			        yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));

			        Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.length);
			        Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

			        Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
			        Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

			        in.copyFrom(yuvByteArray);

			        yuvToRgbIntrinsic.setInput(in);
			        yuvToRgbIntrinsic.forEach(out);

			        out.copyTo(outBytes);			  			        
			        			        
			        Bitmap bmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
			        out.copyTo(bmpout);
			        LuminanceSource source = new RGBLuminanceSource(bmpout);
					BinaryBitmap bitmap = new BinaryBitmap(
							new HybridBinarizer(source));
			        
			        return bitmap;
					
					/*int[] out = new int[framewidth * frameheight];

					CameraFunctions.decodeYUV(out, dataModified,
							framewidth, frameheight);

					Bitmap bMap = Bitmap.createBitmap(out, framewidth,
							frameheight, Config.ARGB_8888);

					LuminanceSource source = new RGBLuminanceSource(bMap);
					BinaryBitmap bitmap = new BinaryBitmap(
							new HybridBinarizer(source));
					return bitmap;*/
				}

				/*
				 * Points: 0- down-left 1- up-left 2- up-right 3- down right
				 */
				private byte[] calculateSmallFrame(byte[] data,
						ResultPoint[] points) {

					byte[] smallframe;

					if (points != null) {
						if (points.length > 1) {
							// distance = ResultPoint.distance(points[0],
							// points[1]);

							minx = points[1].getX(); maxx = points[2].getX();
							miny = points[1].getY(); maxy = points[0].getY();												

							int border = (int) (maxx - minx);

							minx = minx - border;
							maxx = maxx + border;
							miny = miny - border;
							maxy = maxy + border;

							if (minx < 0) minx = 0;
							if (miny < 0) miny = 0;
							if (maxx > width) maxx = width;
							if (maxy > height) maxy = height;

							panel.setMinx((int) minx);
							panel.setMaxx((int) maxx);
							panel.setMiny((int) miny);
							panel.setMaxy((int) maxy);

							// Log.i("CAMERAPREVIEW","##########################VALORES PUNTOS  Puntomin:"+minx+"-"+miny+" Max:"+maxx+"-"+maxy+"   minx: "+minx+"   -maxx:"+maxx+"   -miny:"+miny+"   -maxy:"+maxy+"   -width:"+width+"   -height:"+height+"    -frameWidth:"+(int)(maxx-minx)+"    -frameheight:"+(int)(maxy-miny));

							// smallframe = new
							// byte[(int)((maxx-minx+1)*(maxy-miny+1))*3/2];

							
							smallframe = new byte[width * height * 3];

							for (int i = 0; i < (int) (maxy - miny); i++) {
								// Log.i("CAMERAPREVIEW","##########################VALORES COPIA    sourcePos: "+(int)((miny+i)*width+minx)+"   -destPos:"+(int)(maxx-minx)*i+"   -tam:"+(int)(maxx-minx));
								System.arraycopy(data, (int) ((miny + i)
										* width + minx), smallframe,
										(int) (maxx - minx) * i,
										(int) (maxx - minx));

							}

							framewidth = (int) (maxx - minx) + 1;
							frameheight = (int) (maxy - miny) + 1;

							return smallframe;

						} else
							return data;
					} else
						return data;
				}

			});
		} catch (Exception e) {
			Log.d(TAG, "Error starting camera preview: " + e.getMessage());
		}
	}
}